package com.netx.ebs;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;

import com.netx.generics.basic.Checker;
import com.netx.generics.basic.IntegrityException;
import com.netx.generics.basic.Context;
import com.netx.generics.collections.ImmutableSet;
import com.netx.generics.time.TimeValue;
import com.netx.generics.time.TimeValue.MEASURE;


public class SessionManager {

	private final Map<String,Session> _clientIDs;
	private final Map<Long,Session> _userIDs;
	private final Map<String,NotificationMessage> _notifications;
	
	public Session getSession(long userID) {
		Checker.checkMinValue(userID, 1, "userID");
		return (Session)_userIDs.get(new Long(userID));
	}
	
	public void terminateSession(long userID, String message) {
		terminateSession(getSession(userID), message);
	}
	
	public synchronized void terminateSession(Session s, String message) {
		Checker.checkNull(s, "s");
		Checker.checkEmpty(message, "message");
		terminateSession(s, NOTIFICATION.TERMINATED, message);
	}

	public ImmutableSet<Session> getAllSessions() {
		return new ImmutableSet<Session>(_clientIDs.values());
	}

	// For EbsContext:
	SessionManager() {
		_clientIDs = new HashMap<String,Session>();
		_userIDs = new HashMap<Long,Session>();
		_notifications = new HashMap<String,NotificationMessage>();
	}

	// For AuthenticatorServlet, AuthenticationChecker:
	Session getSession(String clientID) {
		return (Session)_clientIDs.get(clientID);
	}

	// For AuthenticatorServlet, AuthenticationChecker:
	synchronized Session createSession(String clientID, String remoteAddress, User user) {
		Session session = new Session(clientID, remoteAddress, user);
		if(_clientIDs.containsKey(clientID)) {
			throw new IntegrityException(clientID.toString());
		}
		else if(_userIDs.containsKey(new Long(user.getPrimaryKey()))) {
			throw new IntegrityException(user.getPrimaryKey());
		}
		else {
			_clientIDs.put(clientID, session);
			_userIDs.put(new Long(user.getPrimaryKey()), session);
			return session;
		}
	}

	// For AuthenticatorServlet:
	synchronized void terminateSession(Session s, NOTIFICATION n, String message) {
		Context.getRootLogger().info("terminating session "+s+" with "+n);
		if(_clientIDs.remove(s.getClientID()) == null) {
			throw new IntegrityException();
		}
		if(_userIDs.remove(new Long(s.getUser().getPrimaryKey())) == null) {
			throw new IntegrityException();
		}
		_notifications.put(s.getClientID(), new NotificationMessage(s.getUser().getUsername(), n, message));
		s.invalidate();
	}

	// For AuthenticationChecker:
	NotificationMessage getNotificationMessage(String clientID) {
		return (NotificationMessage)_notifications.get(clientID);
	}

	// For AuthenticationChecker:
	void removeNotificationMessage(String clientID) {
		_notifications.remove(clientID);
	}

	// For AuthenticatorServlet:
	void clearNotificationsFor(String clientID) {
		_notifications.remove(clientID);
	}
	
	// For DaemonNotificationChecker:
	void clearNotificationsOlderThan(TimeValue time) {
		Iterator<NotificationMessage> it = _notifications.values().iterator(); 
		while(it.hasNext()) {
			NotificationMessage nm = it.next();
			TimeValue timeElapsed = nm.time.timeElapsed();
			if(timeElapsed.getAs(MEASURE.MILLISECONDS) > time.getAs(MEASURE.MILLISECONDS)) {
				Context.getRootLogger().info("removing notification "+nm.toString());
				_notifications.remove(nm);
			}
		}
	}

	// For DaemonSessionChecker:
	void checkSessionTimeout() {
		Iterator<Session> it = getAllSessions().iterator();
		while(it.hasNext()) {
			Session s = it.next();
			if(s.getUser().getSessionTimeoutTime() != 0) {
				TimeValue timeElapsed = s.getLastAccessed().timeElapsed();
				if(timeElapsed.getAs(MEASURE.MINUTES) > s.getUser().getSessionTimeoutTime()) {
					// Log: info is written in terminateSession
					this.terminateSession(s, NOTIFICATION.TIMEOUT, null);
				}
			}
		}
	}
}
